package top.z.template;

import java.util.*;

/**
 * 模板上下文内容
 */
public class Context {

    private final List<Map<String, Object>> scope = new ArrayList<>();

    /**
     * Keeps track of previously allocated, unused scope.
     * New scope are first tried to be retrieved from this pool to avoid generating garbage.
     */
    private final List<Map<String, Object>> freeScope = new ArrayList<>();

    public Context() {
        push();
    }

    /**
     * 设置变量, 如果某个作用域中已存在该变量, 则设置该变量, 否则, 变量将设置在上次推送的范围上
     */
    public Context set(String name, Object value) {
        for (int i = scope.size() - 1; i >= 0; i--) {
            Map<String, Object> ctx = scope.get(i);
            if (ctx.isEmpty()) {
                continue;
            }
            if (ctx.containsKey(name)) {
                ctx.put(name, value);
                return this;
            }
        }
        scope.get(scope.size() - 1).put(name, value);
        return this;
    }

    /**
     * 在上次推送的作用域上设置变量
     */
    public Context setCurrentScope(String name, Object value) {
        scope.get(scope.size() - 1).put(name, value);
        return this;
    }

    /**
     * 获取变量, 从上到下遍历作用域堆栈
     */
    public Object get(String name) {
        for (int i = scope.size() - 1; i >= 0; i--) {
            Map<String, Object> ctx = scope.get(i);
            if (ctx.isEmpty()) continue;
            Object value = ctx.get(name);
            if (value != null) return value;
        }
        return null;
    }

    /**
     * 返回当前在此上下文中定义的所有变量
     */
    public Set<String> getVariable() {
        Set<String> list = new HashSet<>();
        for (Map<String, Object> value : scope) {
            list.addAll(value.keySet());
        }
        return list;
    }

    /**
     * 将新的'作用域'推送到堆栈上
     */
    public void push() {
        Map<String, Object> _scope;
        if (freeScope.isEmpty()) {
            _scope = new HashMap<>();
        } else {
            _scope = freeScope.remove(freeScope.size() - 1);
        }
        scope.add(_scope);
    }

    /**
     * 删除'作用域'
     */
    public void pop() {
        Map<String, Object> oldScope = scope.remove(scope.size() - 1);
        oldScope.clear();
        freeScope.add(oldScope);
    }
}
